package dryven.init;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import dryven.config.ApplicationConfiguration;
import dryven.i18n.Translator;
import dryven.util.crypt.AESGeneratedKey;
import dryven.util.json.GsonJsonEncoder;
import dryven.util.json.JsonEncoder;
import dryven.model.binding.form.FormDecoder;
import dryven.model.di.DependencyInjectionIndicatorInjector;
import dryven.model.di.DependencyInjector;
import dryven.model.di.DependencyService;
import dryven.model.di.ValueDependencyInjector;
import dryven.model.validation.ValidatorService;
import dryven.model.validation.di.DependencyInjectingValidatorService;
import dryven.persistence.provider.PersistenceProvider;
import dryven.persistence.provider.hibernate.HibernatePersistenceProvider;
import dryven.request.controller.ControllerFactory;
import dryven.request.controller.paramtransform.ActionParameterBindCandidate;
import dryven.request.controller.paramtransform.ActionParameterFillCandidate;
import dryven.request.controller.paramtransform.ActionParameterTransformCandidate;
import dryven.request.controller.paramtransform.baseimpl.DefaultValueFiller;
import dryven.request.controller.paramtransform.baseimpl.EntityInjectingBinder;
import dryven.request.controller.paramtransform.baseimpl.FormValidationNullValueBinder;
import dryven.request.controller.paramtransform.baseimpl.PrimitiveTypeBinder;
import dryven.request.controller.paramtransform.baseimpl.RequestBinder;
import dryven.request.controller.paramtransform.baseimpl.UrlDecodeTransformer;
import dryven.request.routing.Router;
import dryven.request.routing.RoutingService;
import dryven.request.routing.routers.PackageRouter;
import dryven.view.ViewEngine;
import dryven.view.engine.DryvenViewEngine;

public class DefaultFrontControllerFactory extends
		PersistenceFrontControllerFactory {

	private ValidatorService _validatorService;
	private Translator _translator;
	private AESGeneratedKey _aesKey;
	
	@Override
	protected PersistenceProvider createPersistenceProvider() {
		return new HibernatePersistenceProvider(getApplicationConfiguration());
	}

	@Override
	protected void addActionParameterFillers(
			Collection<ActionParameterFillCandidate> fillers) {
		fillers.add(new DefaultValueFiller());
	}

	@Override
	protected void addActionParameterTransformers(
			Collection<ActionParameterTransformCandidate> transformers) {
		//transformers.add(new UrlDecodeTransformer());
	}

	@Override
	protected void addActionParameterBinders(
			Collection<ActionParameterBindCandidate> binders) {
		binders.add(new FormValidationNullValueBinder());
		// important that this one comes before EntityInjectingBinder!
		binders.add(new FormDecoder(getValidatorService(),
				getPersistenceProvider(), 
				getApplicationConfiguration(),
				getAESKey(),
				getApplicationConfiguration().getSignSecret(),getRoutingService()
				));
		// add DI and persistence binders
		super.addActionParameterBinders(binders);
		binders.add(new PrimitiveTypeBinder());
		binders.add(new EntityInjectingBinder(getApplicationConfiguration()));
		binders.add(new RequestBinder());
	}

	@Override
	protected void addRouters(Collection<Router> routers) {
		routers.add(new PackageRouter(getApplicationConfiguration()
				.getControllerPackages()));
	}

	@Override
	protected void addDependencyInjectors(Collection<DependencyInjector> dis) {
		super.addDependencyInjectors(dis);
		dis.add(new ValueDependencyInjector<ControllerFactory>(
				createControllerFactory()));
		dis.add(new ValueDependencyInjector<Translator>(getTranslator()));
		dis.add(new ValueDependencyInjector<DependencyService>(
				getDependencyService()));
		//needed for Form.injectDependencies
		dis.add(new ValueDependencyInjector<AESGeneratedKey>(
				getAESKey()));
		dis.add(new ValueDependencyInjector<ApplicationConfiguration>(
				getApplicationConfiguration()));
		dis.add(new ValueDependencyInjector<ValidatorService>(
				getValidatorService()));
		dis.add(new DependencyInjectionIndicatorInjector(
				getDependencyService()));
		dis.add(new ValueDependencyInjector<ViewEngine>(
				createViewEngine()));
		dis.add(new ValueDependencyInjector<JsonEncoder>(
				createJSONEncoder()));
		/* important that this is the last one, because 
		 * getRoutingService calls addActionParameterBinders,
		 * which in its turn adds all DependencyInjectors added
		 * so far as binders. All injectors that are added after
		 * this call will therefore not be available as routing parameters */
		dis.add(new ValueDependencyInjector<RoutingService>(
				getRoutingService()));
	}

	protected ValidatorService createValidatorService() {
		return new DependencyInjectingValidatorService(getDependencyService(),
				getTranslator());
	}

	protected Translator getTranslator() {
		if (_translator == null) {
			_translator = createTranslator();
		}
		return _translator;
	}

	protected Translator createTranslator() {
		return new Translator() {

			@Override
			public String translate(String string, Object... params) {
				return null;
			}
		};
	}

	protected ValidatorService getValidatorService() {
		if (_validatorService == null) {
			_validatorService = createValidatorService();
		}
		return _validatorService;
	}
	
	protected AESGeneratedKey getAESKey() {
		if(_aesKey==null) {
			_aesKey = new AESGeneratedKey();
		}
		return _aesKey;
	}
	
	protected ViewEngine createViewEngine() {
		List<String> packages = new LinkedList<String>();
		packages.add("java.lang");
		for (String modelPackage : getApplicationConfiguration().getModelPackages()) {
			packages.add(modelPackage);
		}
		return new DryvenViewEngine(getApplicationConfiguration().getViewPackages(), packages);
	}
	
	protected JsonEncoder createJSONEncoder() {
		return new GsonJsonEncoder();
	}
}
